package com.real8.plusplayground.client;

import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.api.gwt.services.plus.shared.Plus;
import com.google.gwt.activity.shared.ActivityManager;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
import com.google.gwt.place.shared.Place;
import com.google.gwt.place.shared.PlaceChangeEvent;
import com.google.gwt.place.shared.PlaceController;
import com.google.gwt.place.shared.PlaceHistoryHandler;
import com.google.gwt.storage.client.Storage;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.web.bindery.event.shared.EventBus;
import com.google.web.bindery.event.shared.UmbrellaException;
import com.google.web.bindery.requestfactory.shared.Receiver;
import com.real8.plusplayground.client.miscellaneous.MiscellaneousPlace;
import com.real8.plusplayground.client.shell.PlusPlaygroundShellView;

public class App {
	private static final String HISTORY_SAVE_KEY = "SAVEPLACE";

	private static final Logger log = Logger.getLogger(App.class.getName());

	private final ClientFactory clientFactory;

	private final Storage storage;
	private final EventBus eventBus;

	private final PlaceController placeController;
	private final ActivityManager activityManager;

	private final AppPlaceHistoryMapper historyMapper;
	private final PlaceHistoryHandler historyHandler;

	private final PlusPlaygroundShellView shell;

	public App(ClientFactory clientFactory, Storage storage, EventBus eventBus,
			PlaceController placeController, ActivityManager activityManager,
			AppPlaceHistoryMapper historyMapper,
			PlaceHistoryHandler historyHandler, PlusPlaygroundShellView shell) {
		this.clientFactory = clientFactory;

		this.storage = storage;
		this.eventBus = eventBus;

		this.placeController = placeController;
		this.activityManager = activityManager;

		this.historyMapper = historyMapper;
		this.historyHandler = historyHandler;

		this.shell = shell;

	}

	/**
	 * Given a parent view to show itself in, start this App.
	 * 
	 * @param parentView
	 *            where to show the app's widget
	 */
	public void run(HasWidgets.ForIsWidget parentView) {
		// set display of activity manager
		activityManager.setDisplay(shell);

		// attach shall to parent
		parentView.add(shell);

		GWT.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
			@Override
			public void onUncaughtException(Throwable e) {
				while (e instanceof UmbrellaException) {
					e = ((UmbrellaException) e).getCauses().iterator().next();
				}

				String message = e.getMessage();
				if (message == null) {
					message = e.toString();
				}
				log.log(Level.SEVERE, "Uncaught exception", e);
				Window.alert("An unexpected error occurred: " + message);
			}
		});

		// create initial anonymous plus RequestFactory
		// TODO consider putting somewhere else
		clientFactory.createNewPlus(new Receiver<Plus>() {
			@Override
			public void onSuccess(Plus response) {
				// init browser history
				initBrowserHistory(new MiscellaneousPlace());
			}
		});
	}

	/**
	 * Initialize browser history / bookmarking. If LocalStorage is available,
	 * use it to make the user's default location in the app the last one seen.
	 */
	private void initBrowserHistory(Place defaultPlace) {
		// check for saved place
		Place savedPlace = null;
		if (storage != null) {
			try {
				// wrap in try-catch in case stored value is invalid
				savedPlace = historyMapper.getPlace(storage
						.getItem(HISTORY_SAVE_KEY));
			} catch (Throwable t) {
				// ignore error and use the default-default
			}
		}
		if (savedPlace == null) {
			savedPlace = defaultPlace;
		}

		// initialize handler
		historyHandler.register(placeController, eventBus, savedPlace);

		// go to first place
		historyHandler.handleCurrentHistory();

		// Monitor the eventbus for place changes and note them in LocalStorage
		// for the next launch.
		if (storage != null) {
			eventBus.addHandler(PlaceChangeEvent.TYPE,
					new PlaceChangeEvent.Handler() {
						public void onPlaceChange(PlaceChangeEvent event) {
							storage.setItem(HISTORY_SAVE_KEY,
									historyMapper.getToken(event.getNewPlace()));
						}
					});
		}
	}
}
